package com.crois.barrier.web.plugin.before;

import com.crois.barrier.common.dto.ApiRouteInfo;
import com.crois.barrier.common.enums.PluginEnum;
import com.crois.barrier.common.enums.PluginTypeEnum;
import com.crois.barrier.plugin.BarrierPluginChain;
import com.crois.barrier.web.cache.CacheManager;
import com.crois.barrier.web.plugin.AbstractBarrierPlugin;
import com.crois.barrier.web.support.AuthUtils;
import com.crois.barrier.web.support.ServerWebExchangeUtils;
import com.zhimydou.cloud.auth.core.exception.InvalidScopeException;
import com.zhimydou.cloud.auth.core.exception.InvalidTokenException;
import com.zhimydou.cloud.auth.core.exception.NotAuthException;
import com.zhimydou.cloud.auth.core.properties.SecurityProperties;
import com.zhimydou.cloud.auth.core.request.AuthAuthentication;
import com.zhimydou.cloud.auth.core.request.Authentication;
import com.zhimydou.cloud.auth.core.token.AuthAccessToken;
import com.zhimydou.cloud.auth.core.token.service.ClientTokenServices;
import com.zhimydou.zlp.constants.TokenConstants;
import com.zhimydou.ztools.collection.CollectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;

public class AuthPlugin extends AbstractBarrierPlugin {

    private static final Logger log = LoggerFactory.getLogger(AuthPlugin.class);

    /**
     * 验证请求url与配置的url是否匹配的工具类
     */
    private AntPathMatcher pathMatcher = new AntPathMatcher();


    private ClientTokenServices clientTokenServices;

    private SecurityProperties securityProperties;

    public AuthPlugin(CacheManager cacheManager,ClientTokenServices clientTokenServices,SecurityProperties securityProperties) {
        super(cacheManager);
        this.clientTokenServices = clientTokenServices;
        this.securityProperties= securityProperties;
    }


    @Override
    protected Mono<Void> doExecute(ServerWebExchange exchange, BarrierPluginChain chain, ApiRouteInfo routeInfo) {
        String requestURI = exchange.getRequest().getURI().getPath();
        log.info("current requestURI:{}",requestURI);
        // 匹配白名单
        if (securityProperties.getIgnore().getHttpUrls().length!=0){
            List<String> includeList = Arrays.asList(securityProperties.getIgnore().getHttpUrls());
            for (String url : includeList) {
                boolean match = pathMatcher.match(url, requestURI);
                if (match){
                    return chain.execute(exchange);
                }
            }
        }

        // 匹配黑名单
        if (securityProperties.getAuth().getHttpUrls().length!=0){
            List<String> excludeList = Arrays.asList(securityProperties.getAuth().getHttpUrls());
            for (String url : excludeList) {
                boolean match = pathMatcher.match(url, requestURI);
                if (match){
                    throw new NotAuthException("请登录！",521);
                }
            }
        }
        String token_header = null;
        // 如果 不是 白名单 和黑名单 则 直接 走拦截器
        if (CollectionUtil.isNotEmpty(exchange.getRequest().getHeaders().get(TokenConstants.AUTH_HEADER))){
            token_header = exchange.getRequest().getHeaders().get(TokenConstants.AUTH_HEADER).get(0);
        }
        if (StringUtils.isNotEmpty(token_header)){
            token_header= AuthUtils.extractToken(exchange.getRequest());
            // 解析token是否有效
            AuthAccessToken token = this.clientTokenServices.readAccessToken(token_header);
            if (token == null) {
                log.error("【授权服务】token:{},令牌未被识别",token_header);
                throw new InvalidTokenException("令牌未被识别");
            } else if (token.isExpired()) {
                log.error("【授权服务】token:{},令牌已过期",token_header);
                throw new InvalidTokenException("令牌已过期");
            }
        }else {
            log.error("【授权服务】请登录");
            throw new NotAuthException("请登录！",521);
        }

        // 判断权限
        AuthAuthentication authAuthentication = clientTokenServices.loadAuthentication(token_header);
        Authentication userAuthentication = authAuthentication.getUserAuthentication();
        List<String> urls = userAuthentication.getUrl();
        if (CollectionUtil.isNotEmpty(urls)){
            boolean result = false;
            for (String url : urls) {
                if (pathMatcher.match(url,requestURI)) {
                    result = true;
                }
            }
            if (!result){
                log.error("【授权服务】请求资源权限不足");
                throw new InvalidScopeException("请求资源权限不足");
            }
        }else {
            log.error("【授权服务】请求资源权限不足");
            throw new InvalidScopeException("请求资源权限不足");
        }
        return chain.execute(exchange);
    }

    @Override
    public PluginTypeEnum pluginType() {
        return PluginTypeEnum.BEFORE;
    }

    @Override
    public String name() {
        return PluginEnum.AUTH.getName();
    }

    @Override
    public int order() {
        return PluginEnum.AUTH.getCode();
    }

    @Override
    public Boolean skip(ServerWebExchange exchange) {
        ApiRouteInfo routeInfo = (ApiRouteInfo) exchange.getAttributes().get(ServerWebExchangeUtils.BARRIER_ROUTE_ATTR);
        if (CollectionUtil.isNotEmpty(routeInfo.getPlugins())){
            return !routeInfo.getPlugins().contains(PluginEnum.AUTH.getName());
        }else {
            return true;
        }
    }
}
